<template>
<div class=" pl-2 pr-1">
<NRow >
<NCol :span="20">
<NSelect class=" " placeholder="选择模型" :default-value="(props.version as string)" :options="[{label:'v3.5',value:'v3.5'},{label:'v4.0',value:'v4.0'}]" @update:value="(v)=>{emits('update:version',v)}"></NSelect>
</NCol>
<NCol :span="4" class=" flex self-center">
<NButton text @click="message.info('同步功能正在制作')"  class=" ml-2">
<NIcon  :size="20" ><div  v-html="SyncIcon"/></NIcon>
</NButton>
</NCol>
</NRow>
<h2 class="w-full text-center">会话记录</h2>
<NButtonGroup class="w-full m-auto" >
<NButton :disabled="props.disabled" class=" w-1/2"  type="primary" @click="()=>{
    handleAddNode(tree.data,{label:'新会话',key:new Date().getTime().toString(),editable:true,isLeaf:true,data:[]},null)
    }">    <template #icon><NIcon><div v-html="MessageIcon"/></NIcon></template>
    新会话
</NButton>
<NButton :disabled="props.disabled" class=" w-1/2" type="info" @click="()=>{
    handleAddNode(tree.data,{label:'新集合',key:new Date().getTime().toString(),editable:true,isLeaf:false},null)
}">
    <template #icon><NIcon><div v-html="FolderAddIcon"/></NIcon></template>
    新集合
</NButton>
</NButtonGroup>
<NDropdown 
:x="contextmenu.x" :y="contextmenu.y"  
:options="contextmenu.options" ref="contextmenuRef" 
:render-icon="contextmenu.renderIcon"
@select="contextmenu.select"
@clickoutside="()=>{tree.showContextmenu=false}"
v-model:show="tree.showContextmenu">
</NDropdown>
<NTree 
@drop="dragDrop" draggable
:render-prefix="(renderPrefix as any)" checkable
:render-suffix="(renderSuffix as any)" block-line ref="treeRef"
:render-label="(renderLabel as any)"
:data="tree.data" 
:on-load="(handleLoad as any)"
:disabled="props.disabled"
:node-props="(nodeProps as any)"
:expanded-keys="tree.expandKeys"
:checked-keys="tree.checkedKeys"
:virtual-scroll="true" style=" height:50vh" class="w-full "></NTree>

</div>
</template>
<script lang="ts" setup>
import { 
    NRow, NTree, NButton, NSelect, NDropdown, DropdownOption,
    NButtonGroup, NIcon, NCol, useDialog, TreeDropInfo, 
    useMessage, NInput } from 'naive-ui'
import type {ChatTreeOption} from "../../../static/js/chatType"
import { h, ref, reactive, watch, VNode } from 'vue';
import SyncIcon from "/src/assets/icons/sync.svg"
import MessageIcon from "/src/assets/icons/message.svg"
import FolderAddIcon from "/src/assets/icons/folder-add.svg"
import DeleteIcon from "/src/assets/icons/delete.svg"
import EditIcon from "/src/assets/icons/edit-square.svg"
import FileIcon from "/src/assets/icons/file.svg"
import FolderIcon from "/src/assets/icons/folder.svg"
import FolderOpenIcon from "/src/assets/icons/folder-open.svg"


interface Props{
    data:ChatTreeOption[],
    displayNode:ChatTreeOption|null,
    disabled:boolean,
    updateKey:any,
    showSuffix: boolean
    version:string
}
const props = withDefaults(defineProps<Props>(),{
    data:()=>[],
    disabled:()=>false,
    updateKey:()=>0,
    showSuffix:()=>false,
    version:()=>'v3.5',
})

const emits = defineEmits<{
    (event:"update:data",value:ChatTreeOption[]):void,
    (event:"update:displayNode",value:ChatTreeOption|null):void,
    (event:"change",status:boolean):void,
    (event:"update:version",value:string):void,
}>()

const message = useMessage()
const dialog = useDialog()
function handleLoad(node:ChatTreeOption){
        return new Promise<void>((resolve) => {
          setTimeout(() => {
            if(node.children){}
            else{
                node.children = [
                ]
            }
            resolve()
          }, 100)
        })
}
function dragDrop(data:TreeDropInfo) {
    let event = data.event as DragEvent
    if(data.dragNode.key!=null){
        if(data.dropPosition == "inside"){
            deleteNode(tree.data,data.dragNode.key.toString())
            data.node.children?.push(data.dragNode)
        }
        if(data.dropPosition == "after" || data.dropPosition == "before"){
            if(data.node.key){
                let parent = findNodeParentByKey(data.node.key.toString(),tree.data,null)
                deleteNode(tree.data,data.dragNode.key.toString())
                let dragNode:any = data.dragNode
                if(parent==null){
                    tree.data.push(dragNode as ChatTreeOption )
                }
                else{
                    parent.children?.push(dragNode as ChatTreeOption )
                }
            }
        }
        emits("update:data",tree.data)
        sortTreeData(tree.data)
    }
}
let tree = reactive({
    data:[] as ChatTreeOption[],
    currentOption:{} as ChatTreeOption,
    displayNode:{} as ChatTreeOption | null,
    expandKeys:[] as string[],
    checkedKeys:[] as string[],
    showContextmenu:false as boolean,
})
let contextmenuRef = ref(null)

let operation = {
    "rename":()=>{
        setTimeout(()=>tree.currentOption.editable=true,10)
    },
    "delete":()=>{
        setTimeout(()=>
        dialog.warning(
            {
                title:"警告",
                content: '确定删除？',
                positiveText: '确定',
                negativeText: '不确定',
                onPositiveClick:()=>{
                    confirmDelete()
                }


            }
        ),10
        )
    },
    "message":()=>{
        setTimeout(()=>{
        createNewConversation(tree.currentOption.key)
            if(!tree.expandKeys.includes(tree.currentOption.key.toString())){
                tree.expandKeys.push(tree.currentOption.key.toString())
            }
        tree.data = treeDataSort(tree.data)
        emits("update:data",tree.data)
        emits('change',true)
        },10)
    },
    "folder":()=>{
        setTimeout(()=>{
        createNewSet(tree.currentOption.key)
            if(!tree.expandKeys.includes(tree.currentOption.key.toString())){
                tree.expandKeys.push(tree.currentOption.key.toString())
            }
        tree.data = treeDataSort(tree.data)
        emits("update:data",tree.data)
        emits('change',true)
        },10)
    }
} as {[key:string]:Function}
let contextmenu = reactive({
    options:[] as DropdownOption[],
    x:0 as number,
    y:0 as number,
    select:(key:string)=>{
        operation[key]()
    },
    renderIcon:(option:DropdownOption)=>{
        let key = option.key as string
        let renderDict = {
            "rename":h(NIcon,{size:20},h('div',{innerHTML:EditIcon})),
            "delete":h(NIcon,{size:20},h('div',{innerHTML:DeleteIcon})),
            "message":h(NIcon,{size:20},h('div',{innerHTML:MessageIcon})),
            "folder":h(NIcon,{size:20},h('div',{innerHTML:FolderAddIcon})),
        } as {[key:string]:VNode}
        return renderDict[key]
    }
})
async function confirmDelete(){
    deleteNode(tree.data,tree.currentOption.key.toString())
    if(tree.displayNode!=null){
        let node = findNodeByKey(tree.displayNode.key.toString(),tree.data)
        if(node==null){
            tree.displayNode = null
        }
    }
    emits("update:data",tree.data);
    emits("update:displayNode",tree.displayNode)
    emits('change',true)
    message.success("删除成功")
}
function nodeProps({ option }: { option: ChatTreeOption }){
    let contextmenuOption = [
        {
            label:"重命名",
            key:"rename",
            disabled:props.disabled
        },
        {
            label:"删除",
            key:"delete",
            disabled:props.disabled
        },
        {
            label:"新会话",
            key:"message",
            disabled:option.isLeaf || props.disabled
        },
        {
            label:"新集合",
            key:"folder",
            disabled:option.isLeaf || props.disabled
        },
    ]
    return {
        onClick:()=>{
            clickNode(option)        
        },
        onKeydown:(e:KeyboardEvent)=>{
            let keyEventDict:{[key:string]:Function} = {
                "Enter":()=>{
                    option.editable = false,
                    emits("change",true)
                }
            }
            if(Object.keys(keyEventDict).includes(e.key)){
                keyEventDict[e.key]()
            }
        },
        onContextmenu:(e: MouseEvent): void =>{
            tree.currentOption = option
            contextmenu.options = contextmenuOption
            tree.showContextmenu = true
            contextmenu.x = e.clientX
            contextmenu.y = e.clientY
            e.preventDefault()
          }
    }
}
async function clickNode(option:ChatTreeOption){
    tree.currentOption = option
    if(!option.isLeaf){
        if(tree.expandKeys.includes(option.key.toString())){
            tree.expandKeys.splice(tree.expandKeys.indexOf(option.key.toString()))
        }
        else{
            tree.expandKeys.push(option.key.toString())
        }
    }
    else{
        tree.checkedKeys = [option.key.toString()]
        tree.displayNode = option;
        emits("update:displayNode",option)
        emits("change",true)
    }
}
function deleteNode(treeData:ChatTreeOption[], key:string) {
    for (let i = 0; i < treeData.length; i++) {
        const node = treeData[i];
        // 如果当前节点的ID匹配要删除的ID，则删除该节点
        if (node.key === key) {
            treeData.splice(i, 1);
            return;
        }
        // 如果当前节点有子节点，则递归调用该函数继续查找
        if (node.children && node.children.length) {
            deleteNode(node.children, key);
        }
    }
    }
function sortTreeData(data:ChatTreeOption[]) {
      return data.sort((a, b) => {
        const isFolderA = a.isLeaf == false;
        const isFolderB = b.isLeaf == false;
        if (isFolderA && !isFolderB) {
            return -1; // Folders first
        } 
        else if (!isFolderA && isFolderB) {
            return 1; // Folders first
        } 
        else {
            var maxLength = Math.max(a.label.length,b.label.length)
            
            return a.label.toLowerCase().padEnd(maxLength).localeCompare(b.label.toLowerCase().padEnd(maxLength)); // Sort alphabetically
        }
      });
    }

function handleAddNode(treeData:ChatTreeOption[],node:ChatTreeOption,parentKey:number|string|null) {
    if(parentKey!=null){
        let parentNode = findNodeByKey(parentKey, treeData);
        if (parentNode!=null) {
            if(parentNode.children==undefined){
                parentNode.children = []
            }
            parentNode.children.push(node); // 在父节点的 children 数组中添加新的节点
            treeData = [...treeData]; // 更新组件的 data 属性
        }
    }
    else{
        treeData.push(node)
    }
}
function findNodeByKey(key:number|string, data:ChatTreeOption[]):ChatTreeOption|null{
    for (let i = 0; i < data.length; i++) {
        let node = data[i];
        if (node.key === key) {
            return node;
        }
        if (node.children) {
            let foundNode = findNodeByKey(key, node.children);
            if (foundNode!=null) {
                return foundNode;
            }
        }
        }
    return null;
}
function findNodeParentByKey(key:number|string, data:ChatTreeOption[],parent:ChatTreeOption|null):ChatTreeOption|null{
    for (let i = 0; i < data.length; i++) {
        let node = data[i];
        if (node.key === key) {
            return parent;
        }
        if (node.children) {
            let foundNode = findNodeParentByKey(key, node.children,node);
            if (foundNode!=null) {
                return foundNode;
            }
        }
        }
    return null;
}
function createNewConversation(key:string|number){
    let parentKey = key as number|string|null
    if(key=="root"){parentKey = null}
    handleAddNode(tree.data,{label:"新会话",key:new Date().getTime().toString(),editable:true,isLeaf:true,data:[]},parentKey)
}
function createNewSet(key:string|number){
    let parentKey = key as number|string|null
    if(key=="root"){parentKey = null}
    handleAddNode(tree.data,{label:"新集合",key:new Date().getTime().toString(),editable:true,isLeaf:false,children:undefined},parentKey)
}

function renderPrefix({ option }: { option: ChatTreeOption }){
    if(option.isLeaf){
        return h(NIcon,{size:20,class:"flex self-center"},h('div',{innerHTML:FileIcon}))
    }
    else{
        if(tree.expandKeys.includes(option.key.toString()) )
        {
            return h(NIcon,{size:20,class:"flex self-center"},h('div',{innerHTML:FolderOpenIcon}))
        }
        else{
            return h(NIcon,{size:20,class:"flex self-center"},h('div',{innerHTML:FolderIcon}))
        }
    }
}
function renderSuffix({ option }:{ option: ChatTreeOption}){
    if(props.showSuffix){
        let buttonGroup = [
            h(NButton,{text:true,onClick:()=>operation['rename']()},
                h(NIcon,{size:20},
                    h('div',{innerHTML:EditIcon}),
                ),
            ),
            h(NButton,{text:true,onClick:()=>operation['delete']()},
                h(NIcon,{size:20},
                h('div',{innerHTML:DeleteIcon}),
                )
            ),
        ]
        if(!option.isLeaf){
            buttonGroup = [
            h(NButton,{text:true,onClick:()=>operation['message']()},
                h(NIcon,{size:20},
                h('div',{innerHTML:MessageIcon}),
                ),
            ),
            h(NButton,{text:true,onClick:()=>operation['folder']()},
                h(NIcon,{size:20},
                h('div',{innerHTML:FolderAddIcon}),
                )
            ),
            ].concat(buttonGroup)
        }
        return h('div',{class:" "},buttonGroup)
    }
}
function renderLabel({ option }:{ option: ChatTreeOption}){
    if(!option.label){option.label=""}
    if(option.editable){
        return h(NInput,{value:option.label,
            onUpdateValue:(v)=>{option.label=v},
            onBlur:()=>{option.editable=false;emits("update:data",tree.data);emits("change",true);message.success("重命名成功")},
            class:"flex self-center"})
    }
    else{
        return option.label
    }
}

function treeDataSort(treeData:ChatTreeOption[]){
    treeData = treeData.sort((a, b) => {
        return a.isLeaf === b.isLeaf ? 
            a.label.localeCompare(b.label, undefined, {numeric: true, sensitivity: 'base'}) : // 叶子节点按照名称排序
            a.isLeaf ? 1 : -1  // 文件夹在前，文件在后
    })
    for(let i=0;i<treeData.length;i++){
        let item = treeData[i];
        if(item.children){
            item.children = treeDataSort(item.children)
        }
    }
    return treeData
}
watch(()=>props.updateKey,()=>{
    tree.data = sortTreeData(props.data) 
    treeDataSort(tree.data)
    emits("update:data",tree.data)
    if(props.displayNode){
        tree.displayNode = props.displayNode
        tree.checkedKeys = [tree.displayNode.key.toString()]
    }
},{deep:true,immediate:true})
</script>